#ifndef STRUCTCELLBASE_HPP
#define STRUCTCELLBASE_HPP

#include "cellBase.hpp"
#include "structMeshFunctions.hpp"
#include <tuple>

namespace hms {

class StructCellTraits {
public:
	using EdgeIndices = Array4i;
};

template<typename CellType>
class StructCellBase : public CellBase<CellType>
{
public:
	using Traits = CellTraits<CellType>;
	using MeshType    = typename Traits::MeshType;
	using EdgeIndices = typename Traits::EdgeIndices;
	
protected:
	const MeshType& m_mesh;
	const Index    m_index;
	const Index    m_i;
	const Index    m_j;

public:
	auto i() const -> Index;
	auto j() const -> Index;

	/**
	 * @brief For a ghost cell, returns the neighbouring domain cell as indices
	 * 
	 * @return std::pair<Index, Index> the horizontal and vertical index of
	 * the neighbouring domain cell
	 */
	// auto domainCell() const -> std::pair<Index, Index>;
	
protected:
	StructCellBase( const MeshType&, Index );
	StructCellBase( const MeshType&, Index, Index );
	StructCellBase( const MeshType&, Index, std::pair<Index, Index> );
	
	auto meshImp      () const -> const MeshType&;
	auto indexImp     () const -> Index;
	auto edgesImp     () const -> EdgeIndices;
	auto edgeImp      () const -> Index;
	auto nEdgesImp    () const -> char;
	auto isGhostImp   () const -> bool;
	auto domainCellImp() const -> CellType;
	auto isBoundaryImp() const -> bool;
	auto neighboursImp() const -> ArrayXi;

	auto verticesImp  (bool ordered) const -> ArrayXi;

	/* these must be provided by any class inheriting from StructCellBase */
	// auto areaImp      () const -> scalar;
	// auto circumfImp   () const -> scalar;
	// auto centroidImp  () const -> FloatVector;
};

template<typename MeshType>
auto toDoubleIndex( const StructMeshBase<MeshType>&, Index cell ) ->
	std::pair<Index, Index>;

template<typename MeshType>
auto toSingleIndex( const StructMeshBase<MeshType>&, Index, Index ) -> Index;


} // end namespace hms

#endif